"""智能合约相关路由"""

from flask import Blueprint, request, current_app
from flasgger import swag_from
from utils.response import create_response
from utils.exceptions import BlockchainMiddlewareException
from config import Config
from models.transaction import TransactionModel
import json
import ast

contract_bp = Blueprint('contract', __name__)

@contract_bp.route('/chains/<chain_type>/contracts/set', methods=['POST'])
@swag_from({
    'tags': ['Contract'],
    'summary': '设置合约数据',
    'description': '在指定区块链上调用合约的set方法',
    'security': [{'ApiKeyAuth': []}],
    'parameters': [
        {
            'name': 'chain_type',
            'in': 'path',
            'type': 'string',
            'required': True,
            'description': '区块链类型 (fisco_bcos, chainmaker, jdchain, xuperchain, thanos)'
        },
        {
            'name': 'set_data',
            'in': 'body',
            'required': True,
            'schema': {
                'type': 'object',
                'properties': {
                    'key': {'type': 'string', 'description': '键'},
                    'value': {'type': 'string', 'description': '值'}
                },
                'required': ['key', 'value']
            }
        }
    ],
    'responses': {
        200: {
            'description': '设置成功',
            'schema': {
                'type': 'object',
                'properties': {
                    'success': {'type': 'boolean'},
                    'data': {
                        'type': 'object',
                        'properties': {
                            'transaction_hash': {'type': 'string'},
                            'result': {'description': '执行结果'}
                        }
                    },
                    'requestId': {'type': 'string'}
                }
            }
        },
        400: {'description': '请求参数错误'},
        500: {'description': '设置失败'}
    }
})
def set_contract_data(chain_type):
    """设置合约数据"""
    try:
        # 验证区块链类型
        supported_chains = ['fisco_bcos', 'chainmaker', 'jdchain', 'xuperchain', 'thanos']
        if chain_type not in supported_chains:
            return create_response(
                success=False,
                error_code='INVALID_CHAIN_TYPE',
                error_message=f'Unsupported chain type: {chain_type}',
                status_code=400
            )
        
        # 获取请求数据
        data = request.get_json()
        if not data:
            return create_response(
                success=False,
                error_code='INVALID_REQUEST',
                error_message='Request body is required',
                status_code=400
            )
        
        key = data.get('key')
        value = data.get('value')
        if not key or not value:
            return create_response(
                success=False,
                error_code='MISSING_PARAMETERS',
                error_message='Key and value are required',
                status_code=400
            )
        
        # 根据不同链类型构造调用参数
        call_data = {'method': 'set'}
        
        if chain_type == 'fisco_bcos':
            call_data.update({
                'params': [key, value]
            })
            # 只有在明确提供时才添加这些参数，否则让适配器使用默认配置
            if data.get('contract_address'):
                call_data['contract_address'] = data.get('contract_address')
            if data.get('user_address'):
                call_data['user_address'] = data.get('user_address')
        elif chain_type == 'chainmaker':
            call_data.update({
                'contract_name': data.get('contract_name', 'simple_storage'),
                'params': {'key': key, 'value': value},
                'call_type': 'invoke'
            })
        elif chain_type == 'jdchain':
            call_data.update({
                'contract_address': data.get('contract_address'),
                'params': [key, value]
            })
        elif chain_type == 'xuperchain':
            # 使用配置中的默认合约名称
            default_contract = Config.SUPPORTED_CHAINS['xuperchain'].get('default_contract', 'simple_storage')
            call_data.update({
                'contract_name': data.get('contract_name', default_contract),
                'params': {'key': key, 'value': value}
            })
        elif chain_type == 'thanos':
            call_data.update({
                'contract_address': data.get('contract_address', 'auto'),
                'params': [key, value]
            })
        
        # 调用统一层的合约方法
        method = call_data.get('method')
        contract_address = call_data.get('contract_address', call_data.get('contract_name', ''))
        params = call_data.get('params', call_data.get('args', []))
        
        # 准备额外的参数
        kwargs = {}
        if chain_type.lower() == 'fisco_bcos':
            # 为FISCO BCOS传递额外的参数，但不重复传递contract_address
            # contract_address已经通过位置参数传递，所以这里不再传递
            if 'user_address' in call_data:
                kwargs['user_address'] = call_data['user_address']
            if 'contract_id' in call_data:
                kwargs['contract_id'] = call_data['contract_id']
        elif chain_type.lower() == 'chainmaker':
            # 为ChainMaker传递call_type参数
            if 'call_type' in call_data:
                kwargs['call_type'] = call_data['call_type']
        
        # 直接调用统一层的同步方法
        result = current_app.unified_layer.call_contract(chain_type, contract_address, method, params, **kwargs)
        
        # 提取tx_hash并持久化交易信息
        tx_hash = _extract_tx_hash(result, chain_type)
        if tx_hash:
            try:
                # 初始化交易模型
                transaction_model = TransactionModel(current_app.mongo_db)
                
                # 保存交易信息到数据库
                transaction_model.save_transaction(
                    tx_hash=tx_hash,
                    chain_type=chain_type,
                    contract_address=contract_address,
                    method=method,
                    params=params,
                    result=result,
                    key=key,
                    value=value
                )
            except Exception as e:
                # 记录错误但不影响主流程
                print(f"保存交易信息失败: {e}")
        
        # 确保返回结果包含tx_hash
        if isinstance(result, dict) and tx_hash:
            result['tx_hash'] = tx_hash
        
        return create_response(data=result)
        
    except BlockchainMiddlewareException as e:
        return create_response(
            success=False,
            error_code=e.error_code,
            error_message=str(e),
            status_code=400
        )
    except Exception as e:
        return create_response(
            success=False,
            error_code='CONTRACT_SET_ERROR',
            error_message=str(e),
            status_code=500
        )

def _extract_tx_hash(result, chain_type):
    """从不同底链的返回结果中提取交易哈希
    
    Args:
        result: 底链返回的结果
        chain_type: 区块链类型
        
    Returns:
        str: 交易哈希，如果未找到返回None
    """
    if not result:
        return None
    
    # 特殊处理：JDChain直接返回字符串格式的tx_hash
    if chain_type == 'jdchain' and isinstance(result, str):
        return result
    
    if not isinstance(result, dict):
        return None
    
    # 根据不同底链的返回格式提取tx_hash
    if chain_type == 'fisco_bcos':
        # FISCO BCOS: {'transactionHash': 'xxx', ...}
        return result.get('transactionHash') or result.get('transaction_hash') or result.get('txHash')
    
    elif chain_type == 'chainmaker':
        # ChainMaker: {'tx_id': 'xxx', ...}
        return result.get('tx_id') or result.get('txId') or result.get('transaction_id')
    
    elif chain_type == 'jdchain':
        # JD Chain: {'txHash': 'xxx', ...}
        return result.get('txHash') or result.get('transaction_hash') or result.get('tx_hash')
    
    elif chain_type == 'xuperchain':
        # XuperChain: {'txid': 'xxx', ...}
        return result.get('txid') or result.get('tx_id') or result.get('transaction_id')
    
    elif chain_type == 'thanos':
        # Thanos: {'hash': 'xxx', ...} 或 {'tx_hash': 'xxx', ...}
        return result.get('hash') or result.get('tx_hash') or result.get('transaction_hash')
    
    # 通用提取逻辑
    common_keys = ['tx_hash', 'txHash', 'transaction_hash', 'transactionHash', 
                   'tx_id', 'txId', 'txid', 'transaction_id', 'hash']
    
    for key in common_keys:
        if key in result and result[key]:
            return result[key]
    
    return None

@contract_bp.route('/chains/<chain_type>/contracts/get', methods=['POST'])
@swag_from({
    'tags': ['Contract'],
    'summary': '获取合约数据',
    'description': '在指定区块链上调用合约的get方法',
    'security': [{'ApiKeyAuth': []}],
    'parameters': [
        {
            'name': 'chain_type',
            'in': 'path',
            'type': 'string',
            'required': True,
            'description': '区块链类型 (fisco_bcos, chainmaker, jdchain, xuperchain, thanos)'
        },
        {
            'name': 'get_data',
            'in': 'body',
            'required': True,
            'schema': {
                'type': 'object',
                'properties': {
                    'key': {'type': 'string', 'description': '键'}
                },
                'required': ['key']
            }
        }
    ],
    'responses': {
        200: {
            'description': '获取成功',
            'schema': {
                'type': 'object',
                'properties': {
                    'success': {'type': 'boolean'},
                    'data': {
                        'type': 'object',
                        'properties': {
                            'result': {'description': '查询结果'},
                            'value': {'type': 'string', 'description': '获取的值'}
                        }
                    },
                    'requestId': {'type': 'string'}
                }
            }
        },
        400: {'description': '请求参数错误'},
        500: {'description': '获取失败'}
    }
})
def get_contract_data(chain_type):
    """获取合约数据"""
    try:
        # 验证区块链类型
        supported_chains = ['fisco_bcos', 'chainmaker', 'jdchain', 'xuperchain', 'thanos']
        if chain_type not in supported_chains:
            return create_response(
                success=False,
                error_code='INVALID_CHAIN_TYPE',
                error_message=f'Unsupported chain type: {chain_type}',
                status_code=400
            )
        
        # 获取请求数据
        data = request.get_json()
        if not data:
            return create_response(
                success=False,
                error_code='INVALID_REQUEST',
                error_message='Request body is required',
                status_code=400
            )
        
        key = data.get('key')
        if not key:
            return create_response(
                success=False,
                error_code='MISSING_KEY',
                error_message='Key is required',
                status_code=400
            )
        
        # 根据不同链类型构造调用参数
        call_data = {'method': 'get'}
        
        if chain_type == 'fisco_bcos':
            call_data.update({
                'params': [key]
            })
            # 只有在明确提供时才添加这些参数，否则让适配器使用默认配置
            if data.get('contract_address'):
                call_data['contract_address'] = data.get('contract_address')
            if data.get('user_address'):
                call_data['user_address'] = data.get('user_address')
            if data.get('contract_id'):
                call_data['contract_id'] = data.get('contract_id')
        elif chain_type == 'chainmaker':
            call_data.update({
                'contract_name': data.get('contract_name', 'simple_storage'),
                'params': {'key': key},
                'call_type': 'query'
            })
        elif chain_type == 'jdchain':
            call_data.update({
                'contract_address': data.get('contract_address'),
                'params': [key]
            })
        elif chain_type == 'xuperchain':
            # 使用配置中的默认合约名称，不再要求传递contract_name
            default_contract = Config.SUPPORTED_CHAINS['xuperchain'].get('default_contract', 'simple_storage')
            call_data.update({
                'contract_name': default_contract,
                'params': {'key': key}
            })
        elif chain_type == 'thanos':
            call_data.update({
                'contract_address': data.get('contract_address', 'auto'),
                'params': [key]
            })
        
        # 调用统一层的合约方法
        method = call_data.get('method')
        contract_address = call_data.get('contract_address', call_data.get('contract_name', ''))
        params = call_data.get('params', call_data.get('args', []))
        
        # 提取额外的kwargs参数
        kwargs = {k: v for k, v in call_data.items() if k not in ['method', 'contract_address', 'contract_name', 'params', 'args']}
        
        # 直接调用统一层的同步方法
        result = current_app.unified_layer.call_contract(chain_type, contract_address, method, params, **kwargs)
        
        # 统一返回结构，提取出 value 字段
        def _extract_value(res):
            if res is None:
                return None
            # 基本类型处理
            if isinstance(res, (str, int, float, bool)):
                # 特殊处理：如果是字符串形式的字典，尝试解析
                if isinstance(res, str) and res.startswith('{') and res.endswith('}'):
                    try:
                        # 尝试解析字符串形式的字典
                        import ast
                        parsed = ast.literal_eval(res)
                        if isinstance(parsed, dict):
                            return _extract_value(parsed)  # 递归处理解析后的字典
                    except (ValueError, SyntaxError):
                        pass  # 解析失败，继续作为普通字符串处理
                return res
            # 字典类型处理多种常见字段
            if isinstance(res, dict):
                # 直接包含 value 的情况
                if 'value' in res:
                    return res.get('value')
                # ChainMaker 常见: contract_result
                if 'contract_result' in res:
                    v = res.get('contract_result')
                    if isinstance(v, dict):
                        # 优先检查 value 字段
                        if 'value' in v:
                            return v.get('value')
                        # 检查 result 字段
                        if 'result' in v:
                            return v.get('result')
                    return v
                # 常见的 result/data 包裹
                for key in ['result', 'data', 'output', 'ret', 'response', 'payload']:
                    if key in res:
                        v = res.get(key)
                        # data 里优先取 value
                        if isinstance(v, dict):
                            if 'value' in v:
                                return v.get('value')
                            # 若仅有一个键，取其值
                            if len(v) == 1:
                                return next(iter(v.values()))
                            return v
                        return v
                # 若只有一个键值，取其值
                if len(res) == 1:
                    return next(iter(res.values()))
                return res
            # 列表类型：XuperChain 可能返回 [{"0": "xxx"}] 或 ["xxx"]
            if isinstance(res, list) and len(res) > 0:
                first = res[0]
                if isinstance(first, dict):
                    if '0' in first:
                        return first['0']
                    # 取第一个值
                    try:
                        return next(iter(first.values()))
                    except Exception:
                        return first
                return first
            # 其他情况，转字符串兜底
            try:
                return json.dumps(res, ensure_ascii=False)
            except Exception:
                return str(res)
        
        value = _extract_value(result)
        if value is not None and not isinstance(value, str):
            value = str(value)
        
        return create_response(data={'value': value})
        
    except BlockchainMiddlewareException as e:
        return create_response(
            success=False,
            error_code=e.error_code,
            error_message=str(e),
            status_code=400
        )
    except Exception as e:
        return create_response(
            success=False,
            error_code='CONTRACT_GET_ERROR',
            error_message=str(e),
            status_code=500
        )